#ifndef __sos_network__H_
#define __sos_network__H_

#ifdef __cplusplus
extern "C"
{
#endif

#include <libwebsockets.h>
#include "cJSON.h"
#include <pthread.h>
#include <dirent.h>
#include <string.h>

// -----------------------------------------------------------------------------------
// 宏定义
// 函数返回状态码定义
#define NW_STATUS_ERR                               -1
#define NW_STATUS_OK                                0
#define NW_STATUS_UNKOWN                            1
#define NW_STATUS_INPUT_PARAM_ERR                   2
#define NW_STATUS_INIT_NOT_OK                       3
#define NW_STATUS_HTTP_CLIENT_QUEUE_IS_FULL         4
#define NW_STATUS_HTTP_URL_HANDES_IS_FULL           5
#define NW_STATUS_NOT_FIND                          6
#define NW_STATUS_WS_CLIENT_QUEUE_IS_FULL           7
#define NW_STATUS_WS_IS_NOT_CONNECTED               8
#define NW_STATUS_DOWNLOAD_TASK_QUEUE_IS_FULL       9

// 日志级别定义
#define NW_LOG_L_ERR                                LLL_ERR
#define NW_LOG_L_WARN                               LLL_WARN
#define NW_LOG_L_NOTICE                             LLL_NOTICE
#define NW_LOG_L_INFO                               LLL_INFO
#define NW_LOG_L_DEBUG                              LLL_DEBUG

// http 方法定义
#define NW_HTTP_METHOD_GET                          "GET"
#define NW_HTTP_METHOD_POST                         "POST"
#define NW_HTTP_METHOD_OPTIONS                      "OPTIONS"
#define NW_HTTP_METHOD_PUT                          "PUT"
#define NW_HTTP_METHOD_PATCH                        "PATCH"
#define NW_HTTP_METHOD_DELETE                       "DELETE"
#define NW_HTTP_METHOD_CONNECT                      "CONNECT"
#define NW_HTTP_METHOD_HEAD                         "HEAD"

// http 方法Id定义
#define NW_HTTP_METHODID_GET                        0
#define NW_HTTP_METHODID_POST                       1  
#define NW_HTTP_METHODID_OPTIONS                    2
#define NW_HTTP_METHODID_PUT                        3
#define NW_HTTP_METHODID_PATCH                      4
#define NW_HTTP_METHODID_DELETE                     5
#define NW_HTTP_METHODID_CONNECT                    6
#define NW_HTTP_METHODID_HEAD                       7


// http status code
#define NW_HTTP_STATUS_CODE_200                      200
#define NW_HTTP_STATUS_CODE_204                      204
#define NW_HTTP_STATUS_CODE_206                      206
// 大于400的是错误响应，其他都是正常响应
#define NW_HTTP_STATUS_CODE_400                      400
#define NW_HTTP_STATUS_CODE_401                      401
#define NW_HTTP_STATUS_CODE_403                      403
#define NW_HTTP_STATUS_CODE_404                      404
#define NW_HTTP_STATUS_CODE_408                      408
// 客户端产生，处理出错了
#define NW_HTTP_STATUS_CODE_418                      418
#define NW_HTTP_STATUS_CODE_500                      500

// 默认token
#define NW_DEFAULT_TOKEN      "8f30c97e4e9911ecb3781831bf6d8bd5"

// ws client status
// 连接成功
#define NW_WS_CLIENT_STATUS_CONNECTED                0
// 连接失败
#define NW_WS_CLIENT_STATUS_CONNECT_FAILED           1
// 收到消息
#define NW_WS_CLIENT_STATUS_RECV_MSG                 2
// 连接断开
#define NW_WS_CLIENT_STATUS_DISCONNECTED             3
// token校验失败导致连接失败
#define NW_WS_CLIENT_STATUS_TOKEN_CHECK_FAILED       4


// nw bool 值定义
#define NW_FALSE                                     0
#define NW_TRUE                                      1

// ws 相关定义
#define NW_WS_HEARTBEAT_MSG                          "sos heartbeat"
// 暂定500ms, 以毫秒为单位
#define NW_WS_HEARTBEAT_INTERVAL                     500

//文件上传固定Url
#define NW_HTTP_SERVER_FILE_UPLOAD_URL               "/device/file/upload"
//文件下载固定Url
#define NW_HTTP_SERVER_FILE_DOWNLOAD_URL               "/device/file/download"


// --------------------------------------------------------
typedef int nw_bool; 

struct nw_ws_client_t;
typedef struct nw_ws_client_t nw_ws;


// -----------------------------------------------------------------------------------
typedef struct {
    int status;
    char *path;
    int contentLen;
    const char *msgBody;
}sos_nw_rsp_t;

typedef struct _nw_list {
	struct _nw_list *next;
	struct _nw_list *prev;
	void *data;
} nw_list_t;

typedef struct {
    char taskId[64];
    char fileName[256];
    char fileMd5[64];
    char savePath[512];
	unsigned long long progressSize;
    nw_list_t *headerResultList;
}FileUploadInfo;

// -----------------------------------------------------------------------------------
// 回调函数原型定义

/* 日志处理hander定义，注册该hander的话，网络模块日志会通过该接口上报给应用层
* level --> 日志级别，参考 NW_LOG_L_ERR 日志级别宏定义
* content --> 日志具体内容
*/
typedef void (*sos_NwLogHander)(int level, const char *content);

/* http client 请求回调函数
* rspBody --> 返回响应接口，具体参考 sos_nw_rsp_t 定义
* userData --> 用户数据，在客户端请求函数时，由使用者传入，回调函数中原样带回
*/
typedef void (*sos_NwHttpRspCb)(sos_nw_rsp_t *rspBody, void *userData);

/* http server 针对某个Url path的处理回调函数，这个需要在网络模块初始化完成后, sos_NwHttpServerRegiserUrlHander 接口注册url的对应处理回调
* methodId --> 请求的方法Id， 参考 NW_HTTP_METHODID_GET 等的定义
* reqBody --> 客户端请求的body内容
* outRspBody --> 出参, rsp 响应的内容， 网络模块会给一个buf进来，长度在 outLen 参数中标识，在回调中，把具体要返回给客户端的内容copy到该buf中，网络
* 模块会把该内容发给客户端
* outLen --> outRspBody buf 的长度
*/
typedef int (*sos_NwHttpServerPathCb)(int methodId, const char* reqBody, char *outRspBody, int outLen);


/* websocket 客户端回调处理， 向服务端
*/
typedef void (*sos_NwWsClientCb)(nw_ws *ws, int status, char *recvMsg, void *data, int msgLength);

/**
 * @fileName       文件名
 * @uploadSize     上传进度，已上传文件大小
 * @status         上传状态
 * */
typedef int (*sos_NwHttpServerUploadCb)(FileUploadInfo *uploadInfo, int status);

typedef enum 
{
    FILE_UPLOAD_OPENED = 0,
    FILE_UPLOAD_WRITING = 1,
    FILE_UPLOAD_FINISHED = 2,
    FILE_UPLOAD_TIMEOUT = 3,
    FILE_UPLOAD_ERROR = 99,
} sos_nw_file_upload_status;

typedef enum {
    NW_PAYLOAD_TEXT = 0,
    NW_PAYLOAD_BINARY = 1
} nw_data_type;

typedef struct {
    //自定义header参数
    char key[64];
    char decodeKey[64];
    char value[1024];
}HeaderItem;

typedef struct {
    //自定义header参数,内容为HeaderItem
    nw_list_t *headerParamList;
    char requestPath[256];
    char savePath[512];
    void *userData;
    sos_NwHttpServerUploadCb uploadCallback;
}FileUploadParam;

// list 
typedef int (*nw_list_compare_func)(const void *, const void*);
typedef void (*nw_list_free_func)(void *);
typedef void (*nw_list_iterate_func)(void *);
typedef void (*nw_list_iterate2_func)(void *, void *);

// timer
typedef void (*nw_timer_cb)(void *timerInstance, void *userData);

// --------------------------------------------------------
typedef enum 
{
    TIMER_TYPE_ONCE = 0,
    TIMER_TYPE_LOOP = 1
} sos_nw_timer_type_e;

typedef struct {
    u_int32_t expireTick;
    u_int32_t intervalTick;
    nw_bool isRel;
    sos_nw_timer_type_e type;
    nw_timer_cb userCb;
    void *userData;
}sos_nw_timer_t;


// -----------------------------------------------------------------------------------
// 接口声明
// 网络模块初始化
int sos_NwInit(sos_NwLogHander hander);

// http req
/* http client 请求接口，
* address --> 服务端地址，可以是ip，也可以是域名，不用带http或https
* port --> 服务端端口 
* path --> 服务端对应的 url path
* method --> 该请求的方法，支持 "POST", "GET", "PUT", "DELETE" 等方法
* body -->  消息体内容， 可以为NULL
* token --> token信息，可以为NULL
* data --> 用户数据，可以为NULL， 会在sos_NwHttpRspCb 回调中原样返回
* https --> 是否为https 请求， 0 -->为http请求， 1 --> 为https请求
* callBack --> 回调接口注册，请求响应结果什么的在该回调函数中通知
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwHttpSendRequest(const char *address, 
                          int port, 
                          const char *path, 
                          const char *method, 
                          const char *body, 
                          const char *token, 
                          void *data, 
                          int https, 
                          sos_NwHttpRspCb callBack);

/* http 上传文件接口，
* address --> 服务端地址，可以是ip，也可以是域名，不用带http或https
* port --> 服务端端口 
* path --> 服务端对应的 url path
* token --> token信息，可以为NULL
* data --> 用户数据，可以为NULL， 会在sos_NwHttpRspCb 回调中原样返回
* fileName --> 要上传的文件名
* fileContent --> 文件内容的bug指针
* contentLen --> 文件内容长度
* https --> 是否为https 请求， 0 -->为http请求， 1 --> 为https请求
* callBack --> 回调接口注册，请求响应结果什么的在该回调函数中通知
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwHttpUploadFile(const char *address, 
                         int port, 
                         const char *path, 
                         const char *token, 
                         char *fileName, 
                         void *fileContent, 
                         int contentLen, 
                         void *data, 
                         int https, 
                         sos_NwHttpRspCb callBack, 
                         char *tag);
/* http 下载文件接口
* address --> 服务端地址，可以是ip，也可以是域名，不用带http或https
* port --> 服务端端口 
* path --> 对应的下载url， 举例：http://ludiqiao-web-home.oss-cn-shenzhen.aliyuncs.com/frpc.tar.gz, 那么对应的path为 /frpc.tar.gz
* token --> token信息，可以为 NULL
* savePath --> 下载文件保存路径， 举例：如果保存路径为 "/tmp" 那么最终文件保存路径为："/tmp/frpc.tar.gz"
* https --> 是否为https 请求， 0 -->为http请求， 1 --> 为https请求
* data --> 用户数据，可以为NULL， 会在sos_NwHttpRspCb 回调中原样返回
* callBack --> 回调接口注册，请求响应结果什么的在该回调函数中通知
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwHttpDownloadFile(const char *address, 
                         int port, 
                         const char *path, 
                         const char *token, 
                         char *savePath, 
                         void *data, 
                         int https, 
                         sos_NwHttpRspCb callBack);


/* 分片下载文件接口, 调用该接口会自动产生多次http请求去分片下载文件内容，整个下载任务超时为60分钟
* address --> 服务端地址，可以是ip，也可以是域名，不用带http或https
* port --> 服务端端口 
* path --> 对应的下载url， 举例：http://ludiqiao-web-home.oss-cn-shenzhen.aliyuncs.com/frpc.tar.gz, 那么对应的path为 /frpc.tar.gz
* saveFilePath --> 文件保存路径，包括文件名。该参数可以是已经存在的文件，如果文件存在，则在该文件的基础上继续下载。如果文件不存在，则在该
                   路径创建一个新文件。
* https --> 是否为https 请求， 0 -->为http请求， 1 --> 为https请求
* data --> 用户数据，可以为NULL， 会在sos_NwHttpRspCb 回调中原样返回
* callBack --> 回调接口注册，请求响应结果什么的在该回调函数中通知
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwHttpDownloadFileRange(const char *address, 
                         int port, 
                         const char *path, 
                         char *saveFilePath, 
                         void *data, 
                         int https, 
                         sos_NwHttpRspCb callBack);

/* 注册某个url的处理函数
* url --> 该处理函数所对应的url
* hander --> 处理回调函数，参考 sos_NwHttpServerPathCb 定义
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwHttpServerRegiserUrlHander(const char *url, sos_NwHttpServerPathCb hander);

/* 启用http 服务端监听，暂时只支持http请求
* port --> 需要监听的本地端口
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwHttpServerListen(int port);

void sos_NwHttpServerLoadFileUploadMoudle(const char *saveDir);
void sos_NwHttpServerRegistFileUploadHandler(FileUploadParam *param);


// ws client start
/* websocket 客户端建链请求
* address --> 服务端地址，可以是ip，也可以是域名，不用带http或https
* port --> 服务端端口 
* path --> 服务端对应的 url path
* token --> token信息
* data --> 用户数据，可以为NULL， sos_NwWsClientCb 回调中原样返回
* https --> 是否为https 请求， 0 -->为http请求， 1 --> 为https请求
* callBack --> 回调接口注册，连接结果以及消息通知等，在该回调中通知
* 返回值 --> 成功返回ws对象，失败返回NULL
*/
nw_ws *sos_NwWsClientConnect(const char *address, 
                             int port, 
                             const char *path, 
                             const char *token, 
                             void *data, 
                             int https, 
                             sos_NwWsClientCb callBack);
/**
 * @headerList   HeaderItem的集合，填需要添加的header
 * 
 * */
nw_ws *sos_NwWsClientConnectWithHeader(const char *address, 
                             int port, 
                             const char *path,  
                             void *data, 
                             int https, 
                             nw_list_t *headerList,
                             sos_NwWsClientCb callBack);

/* ws 发送消息
* ws --> ws对象， 由 sos_NwWsClientConnect函数返回
* msg --> 要发送的消息体内容
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwWsClientSendMsg(nw_ws *ws, char *msg);   

/* 断开ws连接
* ws --> ws对象
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwWsClientDisconnect(nw_ws *ws);

/* 释放ws连接，可以不用disconnect, 在free资源之前会主动调用 disconnect
* ws --> ws对象
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwWsClientFree(nw_ws *ws);

// ws client end

/* 执行系统 shell命令
* cmd --> 命令字符串
* retBuf --> 接收结果的字符串buf
* bufLen --> 结果buf长度
* 返回值： 参考 NW_STATUS_OK 等状态返回值定义
*/
int sos_NwExcCmd(const char *cmd, char *retBuf, int bufLen);


// ---------------------------------- ws servr begin -------------------------------------

#define WS_CONNECT_HEADER_CALLBACK        "wsHeaderCallback"     //header解析结果
#define WS_CONNECT_BUILD_FILTER           "wsConnectFilter"      //连接过滤器，根据此状态处理是否需要建立连接
#define WS_CONNECT_ESTABLISHED            "wsConnected"          //连接已建立成功
#define WS_CONNECT_MSG_RECEIVED           "wsMsgReceived"        //收到消息
#define WS_CONNECT_CLOSED                 "wsClosed"             //连接关闭

struct ClientConnect;

//ws连接
typedef struct ClientConnect WsConnectInfo;

/**
 * @param path                 连接路径
 * @param status               socket连接状态
 * @param attentionHeader      关注的headerParam
 * @param connectInfo          连接详情
 * @param msg                  消息内容
 * @param msgLength            消息长度
 * */
typedef int (*MsgCallback)(const char *path, const char *status, nw_list_t *attentionHeader, 
WsConnectInfo *connectInfo, void *msg, int msgLength);

/**
 * @desc  启动ws server
 * @param port          监听端口
 * */
int ws_server_start(int port);

/**
 * @desc  关闭ws server
 * */
int ws_server_stop();

/**
 * @desc  注册ws server回调
 * @param path          连接路径
 * @param msgCallback   回调接口
 * */
int ws_server_regist_callback(const char *path, MsgCallback msgCallback);

/**
 * @desc  注册ws server回调
 * @param path                 连接路径
 * @param attentionHeader      关注的headerParam
 * @param msgCallback          回调接口
 * */
int ws_server_regist_with_header_callback(const char *path, nw_list_t *attentionHeader, MsgCallback msgCallback);

/**
 * @desc  移除ws server监听
 * @param path                 连接路径
 * */
int ws_server_remove_callback_by_path(const char *path);

/**
 * @desc  发送数据到当前所有连接着的客户端
 * @param data          发送数据
 * @param dataLength    数据长度
 * */
int ws_server_send_to_all(void *data, int dataLength, nw_data_type dataType);

/**
 * @desc  发送数据到指定连接着的客户端
 * @param connectInfo   连接详情
 * @param data          发送数据
 * @param dataLength    数据长度
 * @param dataType      数据类型，NW_PAYLOAD_TEXT：文本类型；NW_PAYLOAD_BINARY：二进制类型
 * */
int ws_server_send_to_specify_client(WsConnectInfo *connectInfo, void *data, int dataLength, nw_data_type dataType);

/**
 * @desc  关闭连接
 * @param connectInfo   连接详情
 * */
int ws_server_close_connect(WsConnectInfo *connectInfo);

/**
 * @desc  获取指定路径的连接数量
 * @param path          连接路径
 * */
int ws_server_get_connect_count(const char *path);

/**
 * @desc               生成header解码相关信息
 * @param key          header key
 * */
HeaderItem *gennerateHeaderItemParam(const char *key);

// ---------------------------------- ws servr end -------------------------------------


// -----------------------------------------------------------------------------------
// 日志函数重定义
#define nw_log_err(...)            lwsl_err(__VA_ARGS__)
#define nw_log_warn(...)           lwsl_warn(__VA_ARGS__)
#define nw_log_notice(...)         lwsl_notice(__VA_ARGS__)
#define nw_log_info(...)           lwsl_info(__VA_ARGS__)
#define nw_log_debug(...)          lwsl_debug(__VA_ARGS__)

// -----------------------------------------------------------------------------------
void *nw_zmalloc(size_t size);
void nw_free(void *object);
// list start
nw_list_t * nw_list_new(void *data);
nw_list_t * nw_list_next(const nw_list_t *elem);
nw_list_t * nw_list_append_link(nw_list_t * elem, nw_list_t *new_elem);
nw_list_t * nw_list_append(nw_list_t * elem, void * data);
void* nw_list_get_data(const nw_list_t *elem);
nw_list_t * nw_list_prepend_link(nw_list_t* elem, nw_list_t *new_elem);
nw_list_t * nw_list_prepend(nw_list_t * elem, void * data);
nw_list_t * nw_list_last_elem(const nw_list_t *l);
nw_list_t * nw_list_first_elem(const nw_list_t *l);
nw_list_t * nw_list_concat(nw_list_t * first, nw_list_t * second);
nw_list_t * nw_list_free(nw_list_t * elem);
nw_list_t * nw_list_free_with_data(nw_list_t *list, nw_list_free_func freefunc);
int nw_list_size(const nw_list_t* first);
nw_list_t * nw_list_unlink(nw_list_t* list, nw_list_t* elem);
nw_list_t * nw_list_erase_link(nw_list_t* list, nw_list_t* elem);
nw_list_t * nw_list_find(nw_list_t* list, const void *data);
nw_list_t * nw_list_remove(nw_list_t * first, void *data);
nw_list_t * nw_list_remove_custom(nw_list_t *first, nw_list_compare_func compare_func, const void *user_data);
nw_list_t * nw_list_pop_front(nw_list_t *list, void **front_data);
nw_list_t * nw_list_find_custom(const nw_list_t* list, nw_list_compare_func compare_func, const void *user_data);
nw_list_t * nw_list_delete_custom(nw_list_t *list, nw_list_compare_func compare_func, const void *user_data);
nw_list_t * nw_list_insert_sorted(nw_list_t* list, void *data, nw_list_compare_func compare_func);
nw_list_t * nw_list_insert(nw_list_t* list, nw_list_t* before, void *data);
void nw_list_for_each(const nw_list_t* list, nw_list_iterate_func func);
void nw_list_for_each2(const nw_list_t* list, nw_list_iterate2_func func, void *user_data);

// timer
sos_nw_timer_t *nw_start_timer(u_int32_t intervalMs, nw_bool isLoop, nw_timer_cb callback, void *userData);
int nw_stop_timer(sos_nw_timer_t *instance);

// file
nw_bool nw_file_exist(const char *filePath);
int nw_file_size(const char *filePath);
int nw_get_file_content(const char* filePath, char *buf, int bufLen);

// rand
int nw_gen_rand_num(int start, int end);

// uuid
int nw_gen_uuid(char *outUuid, int bufLen);


// -------
// test function
void sos_testFunc();
void sos_testFunc2();
void sos_test_http_server2();
void sos_test_ws_client();


#ifdef __cplusplus
}
#endif


#endif 
